package com.biwin.module.jwt.starter.filter;

import cn.hutool.core.util.StrUtil;
import com.biwin.common.api.constant.BwSystemConstant;
import com.biwin.module.jwt.api.exception.TokenAuthenticationException;
import com.biwin.module.jwt.starter.session.BwRedisSessionProvider;
import com.biwin.module.security.api.vo.BwUserDetailVo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.GenericFilterBean;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * BiWin Framework providers Session Manager with Redis
 *
 * @author biwin
 * @since 0.0.1
 */
@Component
public class BwRedisSessionFilter extends GenericFilterBean {
    private static final Logger log = LoggerFactory.getLogger(BwRedisSessionFilter.class);

    @Autowired
    private BwRedisSessionProvider<BwUserDetailVo> redisSessionProvider;

    @Autowired
    private AuthenticationFailureHandler authenticationFailureHandler;

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
            ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String token = resolveToken(httpServletRequest);
        if (StrUtil.isNotBlank(token)) {
            try {
                if (!redisSessionProvider.validateTokenStr(token)) {
                    log.error("非法的请求,使用不合法的Token信息, token: {}", token);
                    throw new TokenAuthenticationException("非法 Token !");
                }
                // 解析当前 token 的真实用户, 刷新 redis 中 token 时间
                Authentication authentication = redisSessionProvider.refreshToken(token);
                ((UsernamePasswordAuthenticationToken) authentication).setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
                SecurityContextHolder.getContext().setAuthentication(authentication);
            } catch (TokenAuthenticationException failed) {
                SecurityContextHolder.clearContext();

                if (logger.isDebugEnabled()) {
                    logger.debug("Token request failed: " + failed.toString(), failed);
                    logger.debug("Updated SecurityContextHolder to contain null Authentication");
                    logger.debug("Delegating to authentication failure handler " + authenticationFailureHandler);
                }

                authenticationFailureHandler.onAuthenticationFailure(httpServletRequest,
                        (HttpServletResponse) response, failed);

                return;
            }
        }
        chain.doFilter(request, response);
    }


    private String resolveToken(HttpServletRequest request) {
        // 约定，仅通过Header方式传输jwt的token
        String bearerToken = request.getHeader(BwSystemConstant.AUTHORIZATION);
        if (StrUtil.isNotBlank(bearerToken) && bearerToken.startsWith(BwSystemConstant.BEARER_TOKEN)) {
            return bearerToken.substring(7);
        }
        return null;
    }
}
